#include "..\common\def.h"
#include "d3d.h"
#include "d3d_util.h"
#include "d3d_mesh.h"
#include "d3d_pointsprite.h"
#include "d3d_particles.h"
#include "d3d_texture.h"
//#include "d3d_light.h"
#include "d3d_image.h"
#include "shaders\d3d_shader.h"
#include "shaders\d3d_depthmapshader.h"
#include "shaders\d3d_lightshader.h"
#include "shaders\d3d_screenshader.h"
#include "..\common\util.h"

#include "interface_types.h"
#include "..\engine\log.h"
#include "..\engine\error.h"
#include "..\engine\engine.h"






const D3DXMATRIX CD3D9Device::m_IdentityMatrix = 
						D3DXMATRIX(	1.0f,0.0f,0.0f,0.0f,
									0.0f,1.0f,0.0f,0.0f,
									0.0f,0.0f,1.0f,0.0f,
									0.0f,0.0f,0.0f,1.0f); 

CD3D9Device::CD3D9Device()
{

	m_BackgroundColor = D3DCOLOR_COLORVALUE(1.0f,1.0f,1.0f,0.0f);
	m_ReflectionPlane = D3DXPLANE(0.0f,1.0f,0.0f, 0.0f);
	m_RenderReflections = false;
	m_RenderSky = false;
	m_RenderLensFlare = false;

	m_pSunSphere = NULL;
	
	EE_ZEROMEMORY(&m_deviceSettings, sizeof(m_deviceSettings)); 
	EE_ZEROMEMORY(&m_pDefaultFont,sizeof(m_pDefaultFont)); 
	
	m_pDefaultFont		= NULL; 
	m_pDefaultShader			= NULL; 
	m_RenderMode = DEFAULT; 
	m_pSpriteBatch = NULL; 

	m_pRTPixelBuffer = NULL; 
	m_pRTBloomBuffer = NULL;
	m_pRTRefractionBuffer = NULL; 
	m_pRTReflectionBuffer = NULL; 
	m_pRTPostProcessBuffer = NULL; 

	m_pScreenQuadVB				= NULL;
	m_pScreenShader				= NULL; 
	m_pScreenColorOverride		= NULL; 

	m_pLightShader				= NULL;
	m_pDepthShader				= NULL; 
	m_pSkyShader				= NULL; 
	m_pScreenSpriteShader		= NULL;

	m_pD3D						= NULL; 
	m_pD3DDevice				= NULL; 
	m_pBackBufferSurface		= NULL; 
	m_pOcclusionQuery				= NULL;
		
	m_pLightTexture = NULL; 
	m_pSkyDome = NULL; 

	m_pDefaultAOTexture = NULL;
	m_pDefaultNMTexture = NULL;
	m_pWhiteTexture = NULL; 
	m_pErrorTexture = NULL; 
	m_pEnvironmentCubemap = NULL;
	for(int i = 0; i < DEVICE_TEXTURE_UNITS; i++)
		m_deviceTextures[i] = NULL;

	m_pCurrentDeclaration		= NULL;
	m_pVertexDeclaration		= NULL; 

}

CD3D9Device::~CD3D9Device(void)	
{
	m_pD3D						= NULL; 
	m_pD3DDevice				= NULL; 
	m_pBackBufferSurface		= NULL; 

}


void CD3D9Device::SetCurrentVertexDeclaration(LPDIRECT3DVERTEXDECLARATION9 pDeclaration)
{
	if(m_pCurrentDeclaration != pDeclaration)
	{
		m_pCurrentDeclaration = pDeclaration;
		m_pD3DDevice->SetVertexDeclaration(pDeclaration);
	}
}

void CD3D9Device::SetRenderMode(const CD3D9Device::RenderMode mode)
{
	m_RenderMode = mode; 
}

CD3D9Device::RenderMode CD3D9Device::GetRenderMode()
{
	return m_RenderMode; 
}


/**************************************
 *Attempts to Create a D3D9 device using
 *the specified settings
 **************************************/
bool CD3D9Device::CreateDevice(LPDIRECT3D9 pD3DInterface, bool fullscreen, int vertexProcessing, D3DDISPLAYMODE& mode, D3DDEVTYPE type, D3DFORMAT format, LPDIRECT3DDEVICE9* pOut)
{
	HRESULT hr;
	D3DPRESENT_PARAMETERS displayParameters; 
	HWND hWnd = NULL;
	LPDIRECT3DDEVICE9 pD3DDevice = NULL;

	do {
		
		if(ISNULL(pOut) || ISNULL(pD3DInterface)) {
			EE_LOGERROR("Invalid arguments.");
			hr = E_INVALIDARG;
			break;
		}

		hWnd = CEngine::GetInstance()->GetWindow()->GetHWnd(); 
		if(ISNULL(hWnd)) {
			EE_LOGERROR("Failed to aquire application window.");
			hr = E_UNEXPECTED;
			break;
		}
		
		EE_ZEROMEMORY(&displayParameters, sizeof(displayParameters)); 
		displayParameters.Windowed					= !fullscreen; 
		displayParameters.SwapEffect				= D3DSWAPEFFECT_DISCARD; 
		displayParameters.hDeviceWindow				= hWnd; 
		displayParameters.BackBufferCount			= 1; 
		displayParameters.MultiSampleQuality		= 0; 
		displayParameters.MultiSampleType			= D3DMULTISAMPLE_NONE; 
		displayParameters.BackBufferFormat			= format; 
		displayParameters.BackBufferWidth			= mode.Width; 
		displayParameters.BackBufferHeight			= mode.Height; 
		displayParameters.Flags						= D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; 
		displayParameters.AutoDepthStencilFormat	= D3DFMT_D16; 
		displayParameters.EnableAutoDepthStencil	= TRUE; 
						
		if(FAILED(pD3DInterface->CreateDevice(D3DADAPTER_DEFAULT, type, hWnd, 
			vertexProcessing, &displayParameters,&pD3DDevice))) {
			hr = E_FAIL;
			break;
		}

		*pOut = pD3DDevice;
		return S_OK;
	
	}while(false);

	if(pOut)
		*pOut = NULL;
	if(pD3DDevice) {
		pD3DDevice->Release();
		pD3DDevice = NULL;
	}
	return hr;
}

/**************************************
 *Enumerates the local hardward to find
 *compatible device settings for rendering
 **************************************/
HRESULT CD3D9Device::EnumerateDevice(LPDIRECT3D9 pD3DInterface, DeviceSettings& settings, LPDIRECT3DDEVICE9* pOut)
{
	D3DDISPLAYMODE			displayMode; 
	D3DCAPS9				deviceCaps; 
	int						deviceType = -1; 
	int						vertexProcessing = -1; 
	int						progress = 0; 
	ArrayList<D3DFORMAT>	formats;

	if(ISNULL(pD3DInterface) || ISNULL(pOut)) {
		EE_LOGERROR("Invalid arguments.");
		return E_INVALIDARG;
	}

	EE_ZEROMEMORY(&displayMode, sizeof(displayMode)); 

	if(pD3DInterface->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode) == D3DERR_INVALIDCALL)
		throw EE_EXCEPTION("GetAdapterDisplayMode failed."); 

	//Itterate over the device types to find one that gives complete caps. Start off
	//with the most preferable type
	for(deviceType = D3DDEVTYPE_HAL;  deviceType < D3DDEVTYPE_NULLREF;  deviceType++)
	{
		vertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING; 

		switch(pD3DInterface->GetDeviceCaps(D3DADAPTER_DEFAULT, (D3DDEVTYPE)deviceType, &deviceCaps))
		{
			case D3D_OK:
				break; 
			case D3DERR_NOTAVAILABLE: 
				throw EE_EXCEPTION("D3D9 not avalible."); 
				break; 
			case D3DERR_INVALIDDEVICE: 
				throw EE_EXCEPTION("Invalid device."); 
				break; 
			case D3DERR_OUTOFVIDEOMEMORY: 
				throw EE_EXCEPTION("Out of video memory."); 
				break; 
		}

		//Find out if the caps for this device type have the basics of what are required
		if(	(deviceCaps.DevCaps & D3DDEVCAPS_HWRASTERIZATION) == false ||
			(deviceCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == false)
			continue; 
		
		//Essential that these are avalible
		if(deviceCaps.VertexShaderVersion < D3DVS_VERSION(3,0) || deviceCaps.PixelShaderVersion < D3DPS_VERSION(3,0))
			continue; 

		//Essential for deferred shading
		if(deviceCaps.NumSimultaneousRTs < 3)
			continue; 
	    if(FAILED(pD3DInterface->CheckDeviceFormat( deviceCaps.AdapterOrdinal, deviceCaps.DeviceType,
			displayMode.Format, D3DUSAGE_RENDERTARGET,
            D3DRTYPE_TEXTURE, D3DFMT_A8R8G8B8)))
			continue; 
	    if(FAILED(pD3DInterface->CheckDeviceFormat( deviceCaps.AdapterOrdinal, deviceCaps.DeviceType,
			displayMode.Format, D3DUSAGE_RENDERTARGET,
            D3DRTYPE_TEXTURE, D3DFMT_R32F)))
			continue; 

		//Assign applicable vertex processing that is supported
		vertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING; 

		progress = 1; 
		break; 
	}

	if(progress != 1)
		throw EE_EXCEPTION("Could not find a suitable device"); 

	//Given the color preferences, look for matching formats
	switch(settings.colorBufferBits)
	{
		case 16:
			formats.Add(D3DFMT_A1R5G5B5);
			formats.Add(D3DFMT_X1R5G5B5); 
			break; 
		case 32:
			formats.Add(D3DFMT_A8R8G8B8);
			formats.Add(D3DFMT_X8R8G8B8); 
			formats.Add(D3DFMT_A2B10G10R10); 
			break; 
		default:
			formats.Add(D3DFMT_X8R8G8B8);
			CLog::Write("Invalid color depth, trying default.\n"); 
			break; 
	}

	//Itterate the avalible formats
	for(unsigned int i = 0;  i < formats.Count();  i++)
	{
		D3DFORMAT colorFormat = formats[i];
		//Itterate the display modes for the color format
		for(unsigned int j = 0, n = pD3DInterface->GetAdapterModeCount(D3DADAPTER_DEFAULT, colorFormat);  j < n;  j++)
		{
			if(pD3DInterface->EnumAdapterModes(D3DADAPTER_DEFAULT, colorFormat, j, &displayMode) != D3D_OK)
				continue; 

			//If the modes screen dimensions match our desired dimensions
			//attempt to use this display mode
			if(	displayMode.Width == settings.screenWidth && 
				displayMode.Height == settings.screenHeight)
			{
				if(SUCCEEDED(CreateDevice(pD3DInterface, settings.isFullscreen, vertexProcessing, displayMode, (D3DDEVTYPE)deviceType, colorFormat, pOut)))
				{
					progress = 2; 
					break; 
				}
			}
		}
	}

	//If we never found a good configuration try a highly compatible configuration
	if(progress != 2)
	{
		if(pD3DInterface->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode) != D3D_OK)
			throw EE_EXCEPTION("GetAdapterDisplayMode failed."); 

		if(CreateDevice(pD3DInterface, false, D3DCREATE_SOFTWARE_VERTEXPROCESSING, displayMode, D3DDEVTYPE_SW, D3DFMT_X8R8G8B8, pOut) == false)
			throw EE_EXCEPTION("Failed to create fallback device."); 

		settings.colorBufferBits = 32; 
		settings.colorBufferBits = 16; 
		settings.isFullscreen = false; 
		settings.screenWidth = displayMode.Width; 
		settings.screenHeight = displayMode.Height; 
	}

	return S_OK; 
}

/**************************************
 *Attempts to initialize default device resources
 *on the assumption that a valid device exists
 **************************************/
HRESULT	CD3D9Device::createDeviceResources()
{
	HRESULT hr;
	LPD3DXSPRITE pSpriteBatch = NULL;
	ScreenText *pDefaultFont = NULL;
	RenderTarget* pRTPixelBuffer = NULL;
	RenderTarget* pRTRefractionBuffer = NULL;
	RenderTarget* pRTPostProcessBuffer = NULL;
	RenderTarget* pRTReflectionBuffer = NULL;
	RenderTarget* pRTBloomBuffer = NULL;

	LPD3DXMESH pSunSphere = NULL;

	Texture* pAOTexture = NULL;
	Texture* pNMTexture = NULL;
	Texture* pWhiteTexture = NULL;
	Texture* pErrorTexture = NULL;

	const D3DFORMAT RTFormat = D3DFMT_A8R8G8B8; 
	cuint RTWidth = m_deviceSettings.screenWidth; 
	cuint RTHeight = m_deviceSettings.screenHeight; 

	do {

		if(ISNULL(m_pD3DDevice)) {
			EE_LOGERROR("Bad device state."); 
			hr = E_UNEXPECTED;
			break;
		}	

		if(FAILED(D3DXCreateSprite(m_pD3DDevice, &pSpriteBatch))) {
			EE_LOGERROR("Failed to create device sprite batch."); 
			hr = E_FAIL;
			break;
		}

		pDefaultFont = EE_NEW ScreenText(this); 
		if(ISNULL(pDefaultFont)) {
			EE_LOGERROR("Failed to allocate device font."); 
			hr = E_OUTOFMEMORY;
			break;
		}			
		
		if(FAILED(createTextFont(pDefaultFont, NULL))) {
			EE_LOGERROR("Failed to initalize default font."); 
			hr = E_FAIL;
			break;
		}

		pRTPixelBuffer = EE_NEW RenderTarget(this);
		pRTReflectionBuffer = EE_NEW RenderTarget(this);
		pRTRefractionBuffer = EE_NEW RenderTarget(this);
		pRTPostProcessBuffer = EE_NEW RenderTarget(this);
		pRTBloomBuffer = EE_NEW RenderTarget(this);

		if(ISNULL(pRTPixelBuffer) || ISNULL(pRTReflectionBuffer) || 
			ISNULL(pRTRefractionBuffer) || ISNULL(pRTPostProcessBuffer) /*||
			ISNULL(pRTOcclusionQuery)*/ ) {
			EE_LOGERROR("Failed to allocate one or more render targets."); 
			hr = E_OUTOFMEMORY;
			break;
		}

		if(FAILED(createRenderTarget(pRTPixelBuffer, RTWidth, RTHeight, RTFormat, false)) ||
			FAILED(createRenderTarget(pRTReflectionBuffer, RTWidth, RTHeight, RTFormat, false)) ||
			FAILED(createRenderTarget(pRTRefractionBuffer, RTWidth, RTHeight, RTFormat, false)) ||
			FAILED(createRenderTarget(pRTPostProcessBuffer, RTWidth, RTHeight, RTFormat, false)) ||
			FAILED(createRenderTarget(pRTBloomBuffer, RTWidth, RTHeight, RTFormat, false)) ) {
			EE_LOGERROR("Failed to create one or more render targets."); 
			hr = E_FAIL;
			break;
		}

		if(FAILED(CreateTexture("defaultao.dds",&pAOTexture)) ||
			FAILED(CreateTexture("defaultnm.dds",&pNMTexture)) ||
			FAILED(CreateTexture(string("white.dds"),&pWhiteTexture)) ||
			FAILED(CreateTexture(string("error.dds"),&pErrorTexture)))
		{
			EE_LOGERROR("Failed to create one or more default device textures."); 
			hr = E_FAIL;
			break;
		}

		if(FAILED(CreateScreenGeometry())) {
			EE_LOGERROR("Failed to create device screen geometry."); 
			hr = E_FAIL;
			break;
		}

		if(FAILED(createDefaultShaders())) {	
			EE_LOGERROR("Failed to create default device shaders."); 
			hr = E_FAIL;
			break;
		}

		if(FAILED(D3DXCreateSphere(m_pD3DDevice, 1.0f, 7, 7, &pSunSphere, NULL))) {
			EE_LOGERROR("Failed to create sun sphere."); 
			hr = E_FAIL;
			break;
		}
		

		m_pSpriteBatch = pSpriteBatch;
		m_pDefaultFont = pDefaultFont;
		m_pRTPixelBuffer = pRTPixelBuffer;
		m_pRTBloomBuffer = pRTBloomBuffer;
		m_pRTRefractionBuffer = pRTRefractionBuffer;
		m_pRTReflectionBuffer = pRTReflectionBuffer;
		m_pRTPostProcessBuffer = pRTPostProcessBuffer;
		m_pSunSphere = pSunSphere;

		m_pDefaultAOTexture = pAOTexture;
		m_pDefaultNMTexture = pNMTexture;
		m_pWhiteTexture = pWhiteTexture;
		m_pErrorTexture = pErrorTexture;

		//TODO: doesn't release old data is exists

		return S_OK;

	}while(false);

	DeregisterResource(pDefaultFont);		
	DeregisterResource(pRTPixelBuffer);
	DeregisterResource(pRTRefractionBuffer);
	DeregisterResource(pRTPostProcessBuffer);
	DeregisterResource(pRTReflectionBuffer);
	DeregisterResource(pAOTexture);
	DeregisterResource(pNMTexture);
	DeregisterResource(pWhiteTexture);
	DeregisterResource(pErrorTexture);

	EE_SAFEDELETE(pDefaultFont);
	EE_SAFEDELETE(pRTPixelBuffer);
	EE_SAFEDELETE(pRTRefractionBuffer);
	EE_SAFEDELETE(pRTReflectionBuffer);
	EE_SAFEDELETE(pAOTexture);
	EE_SAFEDELETE(pNMTexture);
	EE_SAFEDELETE(pWhiteTexture);
	EE_SAFEDELETE(pErrorTexture);

	pSpriteBatch->Release();
	pSpriteBatch = NULL;

	return hr;
}

/**************************************
 *Attempts to initialize a D3D device if
 *it doesn't already exsist
 **************************************/
HRESULT CD3D9Device::Initailize(DeviceSettings& settings)
{
	HRESULT hr;
	LPDIRECT3D9	pD3D = NULL; 
	LPDIRECT3DDEVICE9 pD3DDevice = NULL; 
	LPDIRECT3DSURFACE9 pBackBufferSurface = NULL; 
	LPDIRECT3DQUERY9 pOcclusionQuery = NULL;

	do {
	
		CLog::Write("Creating Direct3D9 device...\n"); 

		//Create a device instance if required
		pD3D = Direct3DCreate9(D3D_SDK_VERSION);
		if(ISNULL(pD3D)) {
			EE_LOGERROR("Failed to create D3D9 interface.");
			hr = E_FAIL;
			break;
		}

		//Find the best device settings avalible on local hardware
		if(FAILED(EnumerateDevice(pD3D, settings, &pD3DDevice))) {
			EE_LOGERROR("Device enumeration failed."); 
			hr = E_FAIL;
		}
		
		//Set the window size according to our resulting settings
		if(FAILED(CEngine::GetInstance()->GetWindow()->SetDimensions(settings.screenWidth, settings.screenHeight))) {
			EE_LOGERROR("Warning: Failed to set window size during D3D device initalization."); 
		}

		m_deviceSettings = settings; 

		//Aquire the back buffer
		if(pD3DDevice->GetRenderTarget(0, &pBackBufferSurface) != D3D_OK) {
			EE_LOGERROR("Failed to aquire device back buffer."); 
			hr = E_FAIL;
			break;
		}

		if(FAILED(pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, NULL))) {
			EE_LOGERROR("Device does not support required hardware query features.");
			hr = E_FAIL;
			break;
		}
		if(FAILED(pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery))) {
			EE_LOGERROR("Failed to create hardware query object.");
			hr = E_FAIL;
			break;
		}

		if(FAILED(Release())) {
			EE_LOGERROR("Failed to release allocated device objects. Memory leaks likely.");
		}

		m_pOcclusionQuery = pOcclusionQuery;
		m_pBackBufferSurface = pBackBufferSurface;
		m_pD3D = pD3D;
		m_pD3DDevice = pD3DDevice;

		if(FAILED(createDeviceResources())) {
			EE_LOGERROR("Failed to create device resources.");
			hr = E_FAIL;
			break;
		}

		//At the moment this stuff is abirtary
		for(int i = 0;  i < 6;  i++)
		{
			m_pD3DDevice->SetSamplerState(i, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); 
			m_pD3DDevice->SetSamplerState(i, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); 
			m_pD3DDevice->SetSamplerState(i, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); 
			m_pD3DDevice->SetSamplerState(i, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); 
			m_pD3DDevice->SetSamplerState(i, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); 
		}

		m_BackgroundColor = D3DCOLOR_COLORVALUE(1.0f,1.0f,1.0f,0.0f);
		//Prevent PIX from profiling this when not a debug build
#ifndef _DEBUG
		D3DPERF_SetOptions(1); 
#endif
		CLog::Write("Success\n"); 
		return S_OK;
	
	}while(false);
	
	if(pBackBufferSurface) {
		pBackBufferSurface->Release();
		pBackBufferSurface = NULL;
	}

	if(pD3DDevice) {
		pD3DDevice->Release();
		pD3DDevice = NULL;
	}
	
	if(pD3D) {
		pD3D->Release();
		pD3D = NULL;
	}

	return hr;
}

/**************************************
 * Searches through the loaded resources
 * and removes the first match found
 **************************************/
HRESULT CD3D9Device::DeregisterResource(IResource* const pResource)
{
	HRESULT status = S_OK; 

	if(ISNULL(pResource))
		return E_FAIL; 

	if(pResource->IsValid())
	{
		if(FAILED(pResource->Release()))
		{
			EE_LOGERROR("Failed to Release resource."); 
			status = E_FAIL; 
		}
	}

	for(uint i = 0; i < m_Textures.Count();  i++)
	{
		Texture* const pSearchResource = m_Textures[i]; 
		if( pSearchResource != pResource ) continue; 
		
		m_Textures.Remove(pSearchResource); 
		return status; 
	}

	for(uint i = 0;  i < m_PointspriteInstances.Count();  i++)
	{
		D3DPointSpriteGroup* const pSearchResource = m_PointspriteInstances[i]; 
		if( pSearchResource != pResource ) continue; 
		
		m_PointspriteInstances.Remove(pSearchResource); 
		return status; 
	}

	for(uint i = 0;  i < m_Sprites.Count();  i++)
	{
		D3DImage* const pSearchResource = m_Sprites[i]; 
		if( pSearchResource != pResource ) continue; 
		
		m_Sprites.Remove(pSearchResource); 
		return status; 	
	}

	for(uint i = 0;  i < m_ParticleSystems.Count();  i++)
	{
		D3DParticleSystem* const pSearchResource = m_ParticleSystems[i]; 
		if( pSearchResource != pResource ) continue; 
		
		m_ParticleSystems.Remove(pSearchResource); 
		return status; 
	}

	for(uint i = 0;  i < m_Lights.Count();  i++)
	{
		D3DLight* const pSearchResource = m_Lights[i]; 
		if( pSearchResource != pResource ) continue; 
		
		m_Lights.Remove(pSearchResource); 
		return status; 
	}

	for(uint i = 0;  i < m_DirectionalLights.Count();  i++)
	{
		D3DDirectionalLight* const pSearchResource = m_DirectionalLights[i]; 
		if( pSearchResource != pResource ) continue; 
		
		m_DirectionalLights.Remove(pSearchResource); 
		return status; 
	}

	for(uint i = 0;  i < m_Models.Count();  i++)
	{
		D3DModel* const pSearchResource = m_Models[i]; 
		if( pSearchResource != pResource ) continue; 
		
		m_Models.Remove(pSearchResource); 
		return status; 
	}

	for(uint i = 0;  i < m_Shaders.Count();  i++)
	{
		D3DShader* const pSearchResource = m_Shaders[i]; 
		if( pSearchResource != pResource ) continue; 
		
		m_Shaders.Remove(pSearchResource); 
		return status; 
	}

	EE_LOGERROR("Failed to locate resource for deregistration.")
	return E_FAIL; 
}

/**************************************
 * Searches through the specified list of 
 * resources and returns S_OK if a matching
 * identtiy is located.
 **************************************/
void CD3D9Device::ReloadResources(ResourceFilter filter)
{
	//Search through cached textures
	if( filter & RESOURCE_TEXTURE)
	{
		for(unsigned int i = 0;  i < m_Textures.Count();  i++)
		{
			IResource* const pSearchResource = m_Textures[i]; 
			if( pSearchResource )
				pSearchResource->Reload(); 
		}
	}

	//Search through cached pointsprites
	if( filter & RESOURCE_POINTSPRITE)
	{
		for(unsigned int i = 0;  i < m_PointspriteInstances.Count();  i++)
		{
			D3DPointSpriteGroup* const pSearchResource = m_PointspriteInstances[i]; 
			if( pSearchResource )
				pSearchResource->Reload(); 
		}
	}

	//Search through cached screensprites
	if( filter & RESOURCE_SCREENSPRITE)
	{
		for(unsigned int i = 0;  i < m_Sprites.Count();  i++)
		{
			D3DImage* const pSearchResource = m_Sprites[i]; 
			if( pSearchResource )
				pSearchResource->Reload(); 
		}
	}

	//Search through cached pointsprites
	if( filter & RESOURCE_PARTICLESYSTEM)
	{
		for(unsigned int i = 0;  i < m_ParticleSystems.Count();  i++)
		{
			IResource* const pSearchResource = m_ParticleSystems[i]; 
			if( pSearchResource )
				pSearchResource->Reload(); 
		}
	}

	//Search through cached lights
	if( filter & RESOURCE_LIGHT)
	{
		for(unsigned int i = 0;  i < m_Lights.Count();  i++)
		{
			IResource* const pSearchResource = m_Lights[i]; 
			if( pSearchResource )
				pSearchResource->Reload(); 
		}
		for(unsigned int i = 0;  i < m_DirectionalLights.Count();  i++)
		{
			IResource* const pSearchResource = m_DirectionalLights[i]; 
			if( pSearchResource )
				pSearchResource->Reload(); 
		}
	}

	//Search through cached models
	if( filter & RESOURCE_MODEL)
	{
		for(unsigned int i = 0;  i < m_Models.Count();  i++)
		{
			IResource* const pSearchResource = m_Models[i]; 
			if( pSearchResource )
				pSearchResource->Reload(); 
		}
	}

	//Search through cached shaders
	if( filter & RESOURCE_SHADER)
	{
		for(unsigned int i = 0;  i < m_Shaders.Count();  i++)
		{
			IResource* const pSearchResource = m_Shaders[i]; 
			if( pSearchResource )
				pSearchResource->Reload(); 
		}

		if(m_pDepthShader)
			m_pDepthShader->Reload();
		if(m_pLightShader)
			m_pLightShader->Reload();
		if(m_pScreenShader)
			m_pScreenShader->Reload();
		if(m_pSkyShader)
			m_pSkyShader->Reload();
	}
}

/**************************************
 * Searches through the resources currently
 * maintained by the render device and returns
 * S_OK if found as well as a pointer to the
 * resource. 
 **************************************/
HRESULT CD3D9Device::SearchForResource(const string& identity, IResource **pResource, 
											 const ResourceFilter filter)
{
	if (ISNULL( pResource ) || !identity.IsValid())
		return E_FAIL; 

	//Search through cached textures
	if( filter & RESOURCE_TEXTURE )
	{
		for(unsigned int i = 0;  i < m_Textures.Count();  i++)
		{
			IResource* const pSearchResource = m_Textures[i]; 
			if( pSearchResource && identity.equals(pSearchResource->Identity()) )
			{
				*pResource = pSearchResource; 
				return S_OK; 
			}
		}
	}

	//Search through cached pointsprites
	if( filter & RESOURCE_POINTSPRITE)
	{
		for(unsigned int i = 0;  i < m_PointspriteInstances.Count();  i++)
		{
			D3DPointSpriteGroup* const pSearchResource = m_PointspriteInstances[i]; 
			if( pSearchResource && identity.equals(pSearchResource->Identity()) )
			{
				*pResource = pSearchResource; 
				return S_OK; 
			}
		}
	}

	//Search through cached screensprites
	if( filter & RESOURCE_SCREENSPRITE)
	{
		for(unsigned int i = 0;  i < m_Sprites.Count();  i++)
		{
			D3DImage* const pSearchResource = m_Sprites[i]; 
			if( pSearchResource && identity.equals(pSearchResource->Identity()) )
			{
				*pResource = pSearchResource; 
				return S_OK; 
			}
		}
	}

	//Search through cached pointsprites
	if( filter & RESOURCE_PARTICLESYSTEM)
	{
		for(unsigned int i = 0;  i < m_ParticleSystems.Count();  i++)
		{
			IResource* const pSearchResource = m_ParticleSystems[i]; 
			if( pSearchResource && identity.equals(pSearchResource->Identity()) )
			{
				*pResource = pSearchResource; 
				return S_OK; 
			}
		}
	}

	//Search through cached lights
	if( filter & RESOURCE_LIGHT)
	{
		for(unsigned int i = 0;  i < m_Lights.Count();  i++)
		{
			IResource* const pSearchResource = m_Lights[i]; 
			if( pSearchResource && identity.equals(pSearchResource->Identity()) )
			{
				*pResource = pSearchResource; 
				return S_OK; 
			}
		}
		for(unsigned int i = 0;  i < m_DirectionalLights.Count();  i++)
		{
			IResource* const pSearchResource = m_DirectionalLights[i]; 
			if( pSearchResource && identity.equals(pSearchResource->Identity()) )
			{
				*pResource = pSearchResource; 
				return S_OK; 
			}
		}
	}

	//Search through cached models
	if( filter & RESOURCE_MODEL)
	{
		for(unsigned int i = 0;  i < m_Models.Count();  i++)
		{
			IResource* const pSearchResource = m_Models[i]; 
			if( pSearchResource && identity.equals(pSearchResource->Identity()) )
			{
				*pResource = pSearchResource; 
				return S_OK; 
			}
		}
	}

	//Search through cached shaders
	if( filter & RESOURCE_SHADER)
	{
		for(unsigned int i = 0;  i < m_Shaders.Count();  i++)
		{
			IResource* const pSearchResource = m_Shaders[i]; 
			if( pSearchResource && identity.equals(pSearchResource->Identity()) )
			{
				*pResource = pSearchResource; 
				return S_OK; 
			}
		}
	}

	*pResource = NULL; 
	return E_FAIL; 
}

/**************************************
 *Creates the resources for a render 
 *target within the specified data structure
 **************************************/
HRESULT CD3D9Device::createRenderTarget(RenderTarget *renderTarget, 
										unsigned int width, unsigned int height, 
										D3DFORMAT colorFormat, bool hasDepthStencil)
{
	LPDIRECT3DSURFACE9		pSurface = NULL; 
	LPD3DXRENDERTOSURFACE	pTargetSurface = NULL; 
	LPDIRECT3DTEXTURE9		pTargetTexture = NULL; 
	D3DSURFACE_DESC			surfaceDescription; 
	try {
		
		//Create a texture for the target so that the targets result can be manipulated
		if(D3DXCreateTexture(m_pD3DDevice, width, height,  1, 
			D3DUSAGE_RENDERTARGET, colorFormat, 
			D3DPOOL_DEFAULT, &pTargetTexture) != D3D_OK)
			throw EE_EXCEPTION( "D3DXCreateTexture failed."); 
		
		if(pTargetTexture->GetSurfaceLevel(0,&pSurface) != D3D_OK)
			throw EE_EXCEPTION( "GetSurfaceLevel failed."); 
		
		if(pSurface->GetDesc(&surfaceDescription) != D3D_OK)
			throw EE_EXCEPTION( "GetDesc failed."); 

		//Create a rendering surface for the target
		if(D3DXCreateRenderToSurface(m_pD3DDevice, surfaceDescription.Width, surfaceDescription.Height, 
			surfaceDescription.Format, hasDepthStencil, D3DFMT_D16, &pTargetSurface) != D3D_OK)
			throw EE_EXCEPTION( "D3DXCreateRenderToSurface failed."); 

		renderTarget->Release(); 
	
		renderTarget->m_pSurface = pSurface; 
		renderTarget->m_pTargetSurface = pTargetSurface; 
		renderTarget->m_pTargetTexture = pTargetTexture; 

		return S_OK; 

	}catch(CError::CErrorData error)
	{
		CError::CError(error); 
		return E_FAIL; 
	}
}

LPDIRECT3DDEVICE9 CD3D9Device::GetDevice()
{
	return m_pD3DDevice; 
}

/**************************************
 *Releases all resources associated with
 *this device
 **************************************/
HRESULT CD3D9Device::releaseResources(void)
{
	unsigned int i = 0; 

	try{

		//Release all textures
		for(i = 0;  i < m_Textures.Count();  i++)
		{
			Texture* pTexture = m_Textures[i]; 
			if(FAILED(pTexture->Release()))
				throw EE_EXCEPTION("Failed to Release texture")
			EE_SAFEDELETE(pTexture); 
		}

		//Release all particle systems
		for(i = 0;  i < m_ParticleSystems.Count();  i++)
		{
			D3DParticleSystem* pSystem = m_ParticleSystems[i]; 
			if(FAILED(pSystem->Release()))
				throw EE_EXCEPTION("Failed to Release particle system"); 
			EE_SAFEDELETE(pSystem); 
		}
		
		//Release all sprite groups
		for(i = 0;  i < m_PointspriteInstances.Count();  i++)
		{
			D3DPointSpriteGroup* pPointSpriteGroup = m_PointspriteInstances[i]; 
			if(FAILED(pPointSpriteGroup->Release()))
				throw EE_EXCEPTION("Failed to Release sprite group"); 
			EE_SAFEDELETE(pPointSpriteGroup); 
		}

		//Release all lights
		for(i = 0;  i < m_Lights.Count();  i++)
		{
			D3DLight* pLight = m_Lights[i]; 
			if(FAILED(pLight->Release()))
				throw EE_EXCEPTION("Failed to Release light"); 
			EE_SAFEDELETE(pLight); 
		}
		for(i = 0;  i < m_DirectionalLights.Count();  i++)
		{
			D3DDirectionalLight* pLight = m_DirectionalLights[i]; 
			if(FAILED(pLight->Release()))
				throw EE_EXCEPTION("Failed to Release directional light"); 
			EE_SAFEDELETE(pLight); 
		}

		//Release all models
		for(i = 0;  i < m_Models.Count();  i++)
		{
			D3DModel* pModel = m_Models[i]; 
			if(FAILED(pModel->Release()))
				throw EE_EXCEPTION("Failed to Release model"); 
			EE_SAFEDELETE(pModel); 
		}
		
		//Release all shaders
		for(i = 0;  i < m_Shaders.Count();  i++)
		{
			D3DShader* m_pShader = m_Shaders[i]; 
			if(FAILED(m_pShader->Release()))
				throw EE_EXCEPTION("Failed to Release shader"); 
			EE_SAFEDELETE(m_pShader); 
		}

		if(m_pRTPixelBuffer) {
			m_pRTPixelBuffer->Release(); 
			m_pRTPixelBuffer = NULL;
		}
		if(m_pRTRefractionBuffer) {
			m_pRTRefractionBuffer->Release(); 
			m_pRTRefractionBuffer = NULL;
		}
		if(m_pRTReflectionBuffer) {
			m_pRTReflectionBuffer->Release();
			m_pRTReflectionBuffer = NULL;
		}
		if(m_pRTPostProcessBuffer) {
			m_pRTPostProcessBuffer->Release(); 
			m_pRTPostProcessBuffer = NULL;
		}

		//Deallocate all memory
		m_Textures.Empty(); 
		m_ParticleSystems.Empty(); 
		m_PointspriteInstances.Empty(); 
		m_Lights.Empty(); 
		m_Models.Empty(); 
		m_Shaders.Empty(); 

		return S_OK; 

	}catch(CError::CErrorData error)
	{
		CError::CError(error); 
		return E_FAIL; 
	}
}

/**************************************
 *Releases all resources associated with
 *this device
 **************************************/
HRESULT CD3D9Device::Release(void)
{
	CLog::Write("Destroying Direct3D device...\n"); 
	m_BackgroundColor = D3DCOLOR_COLORVALUE(1.0f,1.0f,1.0f,0.0f);

	
	if(FAILED(releaseResources()))
		EE_ERROR("Failed to Release resources"); 

	if(FAILED(ReleaseScreenGeometry()))
		EE_ERROR("Creating screen geometry failed."); 

	if(m_pVertexDeclaration != NULL) 
	{
		m_pVertexDeclaration->Release(); 
		m_pVertexDeclaration = NULL;
	}

	if(m_pSpriteBatch)
	{
		if(FAILED(m_pSpriteBatch->Release()))
			EE_LOGERROR("Failed to Release sprite batcher")
		else
			m_pSpriteBatch = NULL; 
	}

	if(m_pSunSphere) {
		if(FAILED(m_pSunSphere->Release()))
			EE_LOGERROR("Failed to release sun sphere.")
		else
			m_pSunSphere = NULL;
	}

	if(m_pOcclusionQuery) {
		if(FAILED(m_pOcclusionQuery->Release()))
			EE_LOGERROR("Failed to release hardware query object.")
		else
			m_pOcclusionQuery = NULL;
	}

	if(m_pBackBufferSurface)
	{
		if(FAILED(m_pBackBufferSurface->Release()))
			EE_LOGERROR("Failed to Release backbuffer")
		else
			m_pBackBufferSurface = NULL; 
	}

	if(m_pD3DDevice)
	{
		if(FAILED(m_pD3DDevice->Release()))
			EE_LOGERROR("Failed to Release Direct3D device")
		else
			m_pD3DDevice = NULL; 
	}

	if(m_pD3D)
	{
		if(FAILED(m_pD3D->Release()))
			EE_LOGERROR("Failed to Release Direct3D interface")
		else
			m_pD3D = NULL; 
	}

	if (!ISNULL(m_pDefaultFont))
	{
		m_pDefaultFont->Release(); 
		EE_SAFEDELETE(m_pDefaultFont); 
	}

	EE_SAFEDELETE( m_pRTPixelBuffer )
	EE_SAFEDELETE(	m_pRTRefractionBuffer )
	EE_SAFEDELETE(m_pRTPostProcessBuffer); 

	CLog::Write("Success\n"); 
	return S_OK; 
}


HRESULT CD3D9Device::CreatePointsprite(const string &filename, const float radius, const Vector3f& position,  D3DSprite** pSpriteInstance)
{
	if(SUCCEEDED(CreatePointsprite(filename, pSpriteInstance)))
	{
		(*pSpriteInstance)->setPosition(position); 
		(*pSpriteInstance)->setRadius(radius); 
		return S_OK; 
	}

	return E_FAIL; 
}

HRESULT CD3D9Device::CreatePointsprite(const string &filename, D3DSprite** pSpriteInstance)
{
	D3DPointSpriteGroup* pPntSprt = NULL; 
	D3DTexture2D *pTexture = NULL; 
	string identity= CUtil::getFilename(filename ); 
	D3DSprite *pSprite = NULL; 
	D3DShader* pShader = NULL;

	try
	{
		if(identity.length() == 0)
			throw EE_EXCEPTION("Invalid mesh name"); 

		if(SUCCEEDED(SearchForResource(identity, (IResource**) &pPntSprt, RESOURCE_POINTSPRITE)))
		{
			if(FAILED(pPntSprt->addPointsprite(&pSprite)))
				throw EE_EXCEPTION("addPointsprite()"); 

			*pSpriteInstance = pSprite; 
			return S_OK; 
		}


		if(FAILED(CreateTexture(filename , (Texture**)&pTexture)))
			throw EE_EXCEPTION("Failed to Create pointsprite D3DTexture2D"); 

		pPntSprt = EE_NEW D3DPointSpriteGroup(this, identity, pTexture); 
		if(ISNULL(pPntSprt))
			throw EE_EXCEPTION("Faild to Create pointsprite group"); 

		if(FAILED(CreateShader((D3DShader**)&pShader, "PointSprite.fx")))
			throw EE_EXCEPTION("Failed to Create default particle shader"); 

		pPntSprt->setShader(pShader);

//		pPntSprt->setShader((D3DShader*) m_pPointSpriteDefault);
		if(FAILED(pPntSprt->addPointsprite(&pSprite)))
			throw EE_EXCEPTION("addPointsprite()"); 
		
		m_PointspriteInstances.Add(pPntSprt); 
		*pSpriteInstance = pSprite; 
		return S_OK; 
	}
	catch(CError::CErrorData errorData)
	{
		CError::CError(errorData, ERRORREPORT_LOG);
		if(pTexture)
			pTexture->Release(); 
		if(pPntSprt)
			pPntSprt->Release(); 
			
		EE_SAFEDELETE(pTexture); 
		EE_SAFEDELETE(pPntSprt); 

		*pSpriteInstance = NULL; 
		return E_FAIL; 
	}	
}

/**************************************
 *Returns the default AO blend texture 
 **************************************/
Texture* CD3D9Device::GetDefaultAOTexture()
{
	return m_pDefaultAOTexture;
}
/**************************************
 *Returns the default NM sample texture 
 **************************************/
Texture* CD3D9Device::GetDefaultNMTexture()
{
	return m_pDefaultNMTexture;
}

/**************************************
 *Returns the black texture which is retained
 *by this device
 **************************************/
Texture* CD3D9Device::GetWhiteTexture()
{
	return m_pWhiteTexture;
}

/**************************************
 *Returns the cubemap of the current envrionemnt
 *if there is one
 **************************************/
D3DTexture3D* CD3D9Device::GetEnvironmentCubemap()
{
	return m_pEnvironmentCubemap;
}

/**************************************
 *Sets the current environment cubemap
 **************************************/
void CD3D9Device::SetEnvironmentCubemap(D3DTexture3D* pCubemap)
{
	m_pEnvironmentCubemap = pCubemap;
}

/**************************************
 *Returns the engines placeholder error 
 *texture 
 **************************************/
Texture* CD3D9Device::GetErrorTexture()
{
	return m_pErrorTexture;
}

/**************************************
 *Returns the deferred light buffer texture 
 *which is retained by this device
 **************************************/
LPDIRECT3DTEXTURE9 CD3D9Device::GetDeferredLightTexture()
{
	return m_pLightTexture;
}

/**************************************
 *Returns the deferred light buffer texture 
 *which is retained by this device
 **************************************/
D3DShader* CD3D9Device::GetDefaultShader()
{
	return m_pDefaultShader;
}

/**************************************
 *Returns the camera view orrientation
 **************************************/
ViewOrrientation* CD3D9Device::GetViewOrrientation()
{
	return &m_camera;
}

/**************************************
 *Returns the renderes device settings
 **************************************/
DeviceSettings CD3D9Device::GetDeviceSettings() const
{
	return m_deviceSettings;
}

/**************************************
 *Enables client specification of special 
 *geometry that is rendered as background
 *scenery
 **************************************/
void CD3D9Device::SetSkydome(D3DMesh *pSkyDome)
{
	m_pSkyDome = pSkyDome; 
}

/**************************************
 *Enables client specification of a global
 *reflection plane for water/ice/etc
 **************************************/
void CD3D9Device::SetReflectionPlane(const Planef& plane)
{
	const Vector3f* pN = &plane.normal;
	m_ReflectionPlane = D3DXPLANE(pN->x,pN->y,pN->z,-plane.distance);
	D3DXPlaneNormalize(&m_ReflectionPlane,&m_ReflectionPlane);
}